package com.frp.lib.handler;

import com.frp.lib.utils.Crypto;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import org.bouncycastle.crypto.StreamCipher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Random;

/**
 * Created by hujianjun on 11/29 0029.
 */
public class AESCFBEncoder extends MessageToMessageEncoder<ByteBuf> {
    private static Logger logger = LoggerFactory.getLogger(AESCFBEncoder.class);

    private String password;
    private String salt = "frp";

    private StreamCipher cipher;

    public AESCFBEncoder(String password) {
        this.password = password;
    }

    public String getSalt() {
        return salt;
    }

    public void setSalt(String salt) {
        this.salt = salt;
    }

    @Override
    protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        if (null == this.cipher) {
            byte[] key = Crypto.encryptPBKDF2(password.toCharArray(), salt.getBytes(), 64, 16 * 8);
            byte[] iv = this.genIv();   //random iv
//            this.cipher = Crypto.buildCipherCFB(key, iv, Cipher.ENCRYPT_MODE);
            this.cipher = Crypto.buildCipherCFB_BC(key, iv, 128, true);
            ByteBuf bf = ctx.alloc().buffer().writeBytes(iv);
//            logger.info("iv:{}", ByteBufUtil.hexDump(bf));
            out.add(bf);
        }
        ByteBuf bf = encrypt(msg);
//        logger.info("msg:{}", ByteBufUtil.hexDump(bf));
        out.add(bf);
    }

    private byte[] genIv() {
//        byte[] iv = new byte[]{
//                54, (byte)134, (byte)185, (byte)230,
//                105, (byte)217, 40, (byte)200,
//                89, (byte)150, 55, 51,
//                (byte)184, (byte)166, (byte)251, 28
//        };
//        return iv;
        byte[] iv = new byte[16];
        Random random = new Random();
        random.nextBytes(iv);
        return iv;
    }

    private ByteBuf encrypt(ByteBuf msg) {
        byte[] bf = new byte[msg.readableBytes()];
        msg.readBytes(bf);
        this.cipher.processBytes(bf, 0, bf.length, bf, 0);
        return Unpooled.wrappedBuffer(bf);
    }

}
